home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d11 / nansi22b.arc / NANSI.ASM < prev    next >
Assembly Source File  |  1987-12-03  |  44KB  |  1,139 lines

  1.         page    66, 132
  2. ;--- nansi.asm ----------------------------------------------------------
  3. ; New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;       nansi.asm   - all DOS function handlers except init
  9. ;       nansi_p.asm - parameter parser for ANSI escape sequences
  10. ;       nansi_f.asm - ANSI command handlers
  11. ;       nansi_i.asm - init DOS function handler
  12. ;
  13. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  14. ; Revision history:
  15. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  16. ; 6  july 85: split off ANSI stuff into other files, added backspace
  17. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  18. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  19. ; 9  aug 85:  added cursor position reporting
  20. ; 10 aug 85:  added output character translation
  21. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  22. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  23. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  24. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  25. ;             direct cursor positioning, takeover of BIOS write_tty,
  26. ;             noticed & squashed 2 related bugs in tab expansion
  27. ; 13 feb 86:  Squashed them again, harder
  28. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  29. ;             If the addition of the beep period to the
  30. ;             BIOS low timer word results in an overflow, the beep will be
  31. ;             supressed. Also made code compatible eith earlier versions
  32. ;             of assembler.
  33. ;------------------------------------------------------------------------
  34. ; Pete Galvin, Austin, TX
  35. ; 12 May 86:  Add code in the dosfn8 routine to avoid creating snow on the
  36. ;             screen.  No check is made for video mode, so use this version
  37. ;             only if your monitor has snow problems (otherwise you will just
  38. ;             run slower than you need to.
  39. ;------------------------------------------------------------------------
  40. ; Keith Jacquemin, Phoenix, AZ
  41. ; 12 June 86: Removed code that was "compatible with earlier versions of
  42. ;             the assembler", The code was in error.
  43. ;
  44. ;             (EGA only) Added code to get the number of crt lines from the
  45. ;             BIOS data area.  The driver failed to recognize the length of
  46. ;             the screen if a program changed it.
  47. ;
  48. ;             Added conditional assembly for Pete Galvin's "snow" code.
  49. ;             Assemble with "/Dsnow" if your screen is subject to snow
  50. ;             on direct video writes.
  51. ;------------------------------------------------------------------------
  52.  
  53.         include nansi_d.asm             ; definitions
  54.  
  55.                                         ; from nansi_f.asm
  56.         extrn   f_escape:near, f_in_escape:near
  57.  
  58.                                         ; from nansi_p.asm
  59.         extrn   param_end:word, redef_end:word
  60.  
  61.                                         ; from nansi_i.asm
  62.         extrn   dosfn0:near
  63.  
  64.                                         ; to nansi_p.asm
  65.         public  f_loopdone
  66.         public  f_not_ansi
  67.         public  f_ansi_exit
  68.  
  69.                                         ; to both nansi_p.asm and nansi_f.asm
  70.         public  cur_x, cur_y, max_x, cur_attrib
  71.  
  72.                                         ; to nansi_f.asm
  73.         public  xy_to_regs, get_blank_attrib
  74.         public  port_6845
  75.         public  wrap_flag
  76.         public  cur_parm_ptr
  77.         public  cur_coords, saved_coords, max_y
  78.         public  escvector, string_term
  79.         public  cpr_esc, cprseq
  80.         public  video_mode
  81.         public  lookup
  82.         public  in_g_mode
  83.  
  84.                                         ; to nansi_i.asm
  85.         public  req_ptr, break_handler
  86.         public  int_29
  87.         IF      takeBIOS
  88.             public  new_vid_bios, old_vid_bios
  89.         ENDIF
  90.  
  91.                                         ; to all modules
  92.         public  xlate_tab_ptr
  93.  
  94. ;--- seg_cs is the CS: override prefix
  95. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  96. seg_cs  macro
  97.         db      2eh
  98.         endm
  99.  
  100. ;--- push_all, pop_all ------------------------------------------------
  101. ; Save/restore all user registers.
  102. push_all macro
  103.         IF      is_8088
  104.             push    ax
  105.             push    bx
  106.             push    cx
  107.             push    dx
  108.             push    bp
  109.             push    si
  110.             push    di
  111.         ELSE
  112.             pusha
  113.         ENDIF
  114.         endm
  115.  
  116. pop_all macro
  117.         IF      is_8088
  118.             pop     di
  119.             pop     si
  120.             pop     bp
  121.             pop     dx
  122.             pop     cx
  123.             pop     bx
  124.             pop     ax
  125.         ELSE
  126.             popa
  127.         ENDIF
  128.         endm
  129.  
  130. keybuf  struc                           ; Used in getchar
  131. len     dw      ?
  132. adr     dw      ?
  133. keybuf  ends
  134.  
  135.  
  136. ABS40   segment at 40h
  137.         org     1ah
  138. buffer_head dw  ?                       ; Used in 'flush input buffer' dos call.
  139. buffer_tail dw  ?
  140.  
  141.         org     49h
  142. crt_mode db     ?
  143. crt_cols dw     ?
  144. crt_len dw      ?
  145. crt_start dw    ?
  146. cursor_posn dw  8 dup (?)
  147. cursor_mode dw  ?
  148. active_page db  ?
  149. addr_6845 dw    ?
  150. crt_mode_set db ?                       ; = 7 only if monochrome display adaptor
  151. crt_palette db  ?
  152.         org     6ch
  153. timer_low dw    ?                       ; low word of time-of-day counter (18.2 hz)
  154.         org     84h
  155. crt_rows db     ?
  156.  
  157. ABS40   ends
  158.  
  159.         page
  160.  
  161. CODE    segment byte public 'CODE'
  162. assume  cs:code, ds:code
  163.  
  164.                                         ; Device Driver Header
  165.  
  166.         org     0
  167.  
  168.         dd      -1                      ; next device
  169.         dw      8013h                   ; attributes
  170.         dw      strategy                ; request header pointer entry
  171.         dw      interrupt               ; request entry point
  172.         db      "CON     "              ; device name (8 char)
  173.  
  174.                                         ; Identification- in case somebody TYPEs the assembled driver
  175.         db      27, '[2J'
  176.         db      "Nansi.sys v2.2B"
  177.         IFE     is_8088
  178.             db      "(80286)"
  179.         ENDIF
  180.         db      ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  181.         db      13, 10, 26
  182.  
  183.  
  184. ;----- variable area --------------------
  185. req_ptr label   dword
  186. req_off dw      ?
  187. req_seg dw      ?
  188.  
  189. wrap_flag db    1                       ; 0 = no wrap past line end
  190. escvector dw    0                       ; state vector of ESCape sequencor
  191. video_mode db   3                       ; ROM BIOS video mode (2=BW, 3=color)
  192. max_y   db      24
  193. max_cur_x label word                    ; used to get both max & cur at once
  194. max_x   db      79                      ; line width (79 for 80x25 modes)
  195. cur_coords label word
  196. cur_x   db      0                       ; cursor position (0 = left edge)
  197. cur_y   db      0                       ;                 (0 = top edge)
  198. saved_coords dw ?                       ; holds XY after a SCP escape sequence
  199. string_term db  0                       ; either escape or double quote
  200. cur_attrib db   7                       ; current char attributes
  201. cur_page db     0                       ; current display page
  202. video_seg dw    ?                       ; segment of video card
  203. f_cptr_seg dw   ?                       ; part of fastout write buffer pointer
  204. cur_parm_ptr dw ?                       ; last byte of parm area now used
  205. port_6845 dw    ?                       ; port address of 6845 card
  206. xlate_tab_ptr dw ?                      ; pointer to output translation table
  207.         IF      takeBIOS
  208. old_vid_bios dd     ?                   ; pointer to old video bios routine
  209.         ENDIF
  210.  
  211. brkkeybuf db    3                       ; control C
  212. fnkeybuf db     ?                       ; holds second byte of fn key codes
  213. cpr_buf db      8 dup (?), '['
  214. cpr_esc db      1bh                     ; descending buffer for cpr function
  215.  
  216. ; following four keybufs hold information about input
  217. ; Storage order determines priority- since the characters making up a function
  218. ; key code must never be separated (say, by a Control-Break), they have the
  219. ; highest priority, and so on.  Keyboard keys (except ctrl-break) have the
  220. ; lowest priority.
  221.  
  222. fnkey   keybuf  <0, fnkeybuf>           ; fn key string (0 followed by scan code)
  223. cprseq  keybuf  <0>                     ; CPR string (ESC [ y;x R)
  224. brkkey  keybuf  <0, brkkeybuf>          ; ^C
  225. xlatseq keybuf  <0>                     ; keyboard reassignment string
  226.  
  227. ;------ xy_to_regs --------------------------------------------
  228. ; on entry: x in cur_x, y in cur_y
  229. ; on exit:  dx = chars left on line, di = address
  230. ; Alters ax, bx.
  231. xy_to_regs proc near
  232.                                         ; Find number of chars 'till end of line, keep in DX
  233.         mov     ax, max_cur_x
  234.         mov     bx, ax                  ; save max_x & cur_x for next block
  235.         mov     ah, 0                   ; ax = max_x
  236.         xchg    dx, ax
  237.         mov     al, bh
  238.         mov     ah, 0                   ; ax = cur_x
  239.         sub     dx, ax
  240.         inc     dx                      ; dx is # of chars till EOL
  241.                                         ; Calculate DI = current address in text buffer
  242.         mov     al, bl                  ; al = max_x
  243.         inc     al
  244.         mul     cur_y
  245.         add     al, bh                  ; al += cur_x
  246.         adc     ah, 0                   ; AX is # of chars into buffer
  247.         add     ax, ax
  248.         xchg    di, ax                  ; DI is now offset of cursor.
  249.         ret
  250. xy_to_regs endp
  251.  
  252.  
  253. ;------- dos_fn_tab -------------
  254. ; This table is used in "interrupt" to call the routine that handles
  255. ; the requested function.
  256.  
  257. max_cmd equ     12
  258. dos_fn_tab:
  259.         dw      dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  260.         dw      dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  261.  
  262. ;------- strategy ----------------------------------------------------
  263. ; DOS calls strategy with a request which is to be executed later.
  264. ; Strategy just saves the request.
  265.  
  266. strategy proc   far
  267.         mov     cs:req_off,BX
  268.         mov     cs:req_seg,ES
  269.         ret
  270. strategy endp
  271.  
  272. ;------ interrupt -----------------------------------------------------
  273. ; This is where the request handed us during "strategy" is
  274. ; actually carried out.
  275. ; Calls one of 12 subroutines depending on the function requested.
  276. ; Each subroutine returns with exit status in AX.
  277.  
  278. interrupt proc  far
  279.         sti
  280.         push_all                        ; preserve caller's registers
  281.         push    ds
  282.         push    es
  283.  
  284.                                         ; Read requested function information into registers
  285.         lds     bx,cs:req_ptr
  286.         mov     al,[BX+02h]             ; al = function code
  287.         les     si,[BX+0Eh]             ; ES:SI = input/output buffer addr
  288.         mov     cx,[BX+12h]             ; cx = input/output byte count
  289.  
  290.         cmp     al, max_cmd
  291.         ja      unk_command             ; too big, exit with error code
  292.  
  293.         xchg    bx, ax
  294.         shl     bx, 1                   ; form index to table of words
  295.         mov     ax, cs
  296.         mov     ds, ax
  297.         call    word ptr dos_fn_tab[bx]
  298. int_done:
  299.         lds     bx,cs:req_ptr           ; report status
  300.         or      ax, 100h                ; (always set done bit upon exit)
  301.         mov     [bx+03],ax
  302.  
  303.         pop     ES                      ; restore caller's registers
  304.         pop     DS
  305.         pop_all
  306.         ret                             ; return to DOS.
  307.  
  308. unk_command:
  309.         call    badcmd
  310.         jmp     int_done
  311.  
  312. interrupt endp
  313.  
  314. ;----- BIOS break handler -----------------------------------------
  315. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  316. ; Simply notes that a break was hit.  Flag is checked during input calls.
  317.  
  318. break_handler proc
  319.         mov     cs:brkkey.len, 1
  320.         iret
  321. break_handler endp
  322.  
  323.         page
  324.  
  325. ;------ badcmd -------------------------------------------------------
  326. ; Invalid function request by DOS.
  327. badcmd  proc    near
  328.         mov     ax, 813h                ; return "Error: invalid cmd"
  329.         ret
  330. badcmd  endp
  331.  
  332.  
  333. ;------ nopcmd -------------------------------------------------------
  334. ; Unimplemented or dummy function request by DOS.
  335. nopcmd  proc    near
  336.         xor     ax, ax                  ; No error, not busy.
  337.         ret
  338. nopcmd  endp
  339.  
  340. ;------- dos function #4 ----------------------------------------
  341. ; Reads CX characters from the keyboard, places them in buffer at
  342. ; ES:SI.
  343. dosfn4  proc    near
  344.         jcxz    dos4done
  345.         mov     di, si
  346. dos4lp: push    cx
  347.         call    getchar
  348.         pop     cx
  349.         stosb
  350.         loop    dos4lp
  351. dos4done:
  352.         xor     ax, ax                  ; No error, not busy.
  353.         ret
  354. dosfn4  endp
  355.  
  356. ;-------- dos function #5: non-destructive input, no wait ------
  357. ; One-character lookahead into the keyboard buffer.
  358. ; If no characters in buffer, return BUSY; otherwise, get value of first
  359. ; character of buffer, stuff into request header, return DONE.
  360. dosfn5  proc    near
  361.         call    peekchar
  362.         jz      dos5_busy
  363.  
  364.         lds     bx,req_ptr
  365.         mov     [bx+0Dh], al
  366.         xor     ax, ax                  ; No error, not busy.
  367.         jmp     short dos5_exit
  368. dos5_busy:
  369.         MOV     ax, 200h                ; No error, busy.
  370. dos5_exit:
  371.         ret
  372.  
  373. dosfn5  endp
  374.  
  375. ;-------- dos function #6: input status --------------------------
  376. ; Returns "busy" if no characters waiting to be read.
  377. dosfn6  proc    near
  378.         call    peekchar
  379.         mov     ax, 200h                ; No error, busy.
  380.         jz      dos6_exit
  381.         xor     ax, ax                  ; No error, not busy.
  382. dos6_exit:
  383.         ret
  384. dosfn6  endp
  385.  
  386. ;-------- dos function #7: flush input buffer --------------------
  387. ; Clears the IBM keyboard input buffer.  Since it is a circular
  388. ; queue, we can do this without knowing the beginning and end
  389. ; of the buffer; all we need to do is set the tail of the queue
  390. ; equal to the head (as if we had read the entire queue contents).
  391. ; Also resets all the device driver's stuffahead buffers.
  392. dosfn7  proc    near
  393.         xor     ax, ax
  394.         mov     fnkey.len, ax           ; Reset the stuffahead buffers.
  395.         mov     cprseq.len, ax
  396.         mov     brkkey.len, ax
  397.         mov     xlatseq.len, ax
  398.  
  399.         mov     ax, abs40
  400.         mov     es, ax
  401.         mov     ax, es:buffer_head      ; clear queue by making the tail
  402.         mov     es:buffer_tail, ax      ; equal to the head
  403.  
  404.         xor     ax, ax                  ; no error, not busy.
  405.         ret
  406. dosfn7  endp
  407.  
  408.         page
  409.         IF      takeBIOS
  410. ;--- new_vid_bios -------------------------------------------
  411. ; New_vid_bios simply replaces the write_tty call.
  412. ; All other calls get sent to the old video bios.
  413. ; This gives BIOS ANSI capability.
  414. ; However, it takes away the escape character.
  415. ; If this is not desired, just tell init to not take over the vector.
  416.  
  417. new_vid_bios proc
  418.             cmp     ah, 14
  419.             jz      nvb_write_tty
  420.             jmp     dword ptr cs:old_vid_bios
  421. nvb_write_tty:
  422.             push    cx
  423.             mov     cl, cs:cur_attrib
  424.                                         ; If in graphics mode, BL is new color
  425.             call    in_g_mode           ; returns carry set if text mode
  426.             jc      nvb_wt_text
  427.             mov     cs:cur_attrib, bl   ; ja?
  428. nvb_wt_text:
  429.             int     29h                 ; write AL
  430.             mov     cs:cur_attrib, cl   ; restore color
  431.             pop     cx
  432.             iret
  433.  
  434. new_vid_bios endp
  435.         ENDIF
  436.  
  437. ;------ int_29 ----------------------------------------------
  438. ; Int 29 handles DOS quick-access putchar.
  439. ; Last device loaded with attribute bit 4 set gets accessed for
  440. ; single-character writes via int 29h instead of via interrupt.
  441. ; Must preserve all registers.
  442. ; Installed as int 29h by dosfn0 (init).
  443. int_29_buf db   ?
  444.  
  445. int_29  proc    near
  446.         sti
  447.         push    ds
  448.         push    es
  449.         push_all
  450.         mov     cx, 1
  451.         mov     bx, cs
  452.         mov     es, bx
  453.         mov     ds, bx
  454.         mov     si, offset int_29_buf
  455.         mov     byte ptr [si], al
  456.         call    dosfn8
  457.         pop_all
  458.         pop     es
  459.         pop     ds
  460.         iret
  461. int_29  endp
  462.  
  463.         page
  464. ;------ dosfn8 -------------------------------------------------------
  465. ; Handles writes to the device (with or without verify).
  466. ; Called with
  467. ;  CX    = number of bytes to write
  468. ;  ES:SI = transfer buffer
  469. ;  DS    = CS, so we can access local variables.
  470.  
  471. dosfn8  proc    near
  472.  
  473.         mov     f_cptr_seg, es          ; save segment of char ptr
  474.  
  475.                                         ; Read the BIOS buffer address/cursor position variables.
  476.         mov     ax,abs40
  477.         mov     ds,ax
  478.         assume  ds:abs40
  479.  
  480.                                         ; Find current video mode and screen size.
  481.         mov     ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  482.         mov     cs:video_mode, al
  483.         dec     ah                      ; ah = max column
  484.         mov     cs:max_x, ah
  485.         mov     al,crt_rows
  486.         or      al,al
  487.         jz      oldcrd
  488.         mov     cs:max_y,al
  489. oldcrd:
  490.  
  491.                                         ; Find current cursor coordinates.
  492.         mov     al,active_page
  493.         cbw
  494.         add     ax,ax
  495.         xchg    bx,ax
  496.         mov     ax,cursor_posn[bx]
  497.         mov     cs:cur_coords,AX
  498.  
  499.                                         ; Find video buffer segment address; adjust it
  500.                                         ; so the offset is zero; return in AX.
  501.  
  502.                                         ; DS is abs40.
  503.                                         ; Find 6845 address.
  504.         mov     ax, addr_6845
  505.         mov     cs:port_6845, ax
  506.                                         ; Find video buffer address.
  507.         MOV     AX,crt_start
  508.         shr     ax, 1
  509.         shr     ax, 1
  510.         shr     ax, 1
  511.         shr     ax, 1
  512.         add     ah, 0B0h                ; assume it's a monochrome card...
  513.         CMP     cs:video_mode,07
  514.         jz      d8_gots
  515.         add     ah, 8                   ; but if not mode 7, it's color.
  516. d8_gots:
  517.         push    cs
  518.         pop     ds
  519.         assume  ds:code
  520.         mov     video_seg, ax
  521.         mov     es, ax
  522.         call    xy_to_regs              ; Set DX, DI according to cur_coords.
  523.  
  524.                                         ; | If in graphics mode, clear old pseudocursor
  525.         call    in_g_mode
  526.         jc      d8_no_cp
  527.         call    pseudocursor            ; write block in xor
  528. d8_no_cp:
  529.         mov     bx, xlate_tab_ptr       ; get pointer to translation table
  530.  
  531.         mov     ah, cur_attrib
  532.         mov     ds, f_cptr_seg          ; get segment of char ptr
  533.         assume  ds:nothing
  534.         cld                             ; make sure we'll increment
  535.  
  536.                                         ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  537.                                         ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  538.                                         ; At that speed, it takes 32 milliseconds to fill a screen.
  539.  
  540.                                         ; Get a character, put it on the screen, repeat 'til end of line
  541.                                         ; or no more characters.
  542.         jcxz    f_loopdone              ; if count = 0, we're already done.
  543.         cmp     cs:escvector, 0         ; If in middle of an escape sequence,
  544.         jnz     f_in_escapex            ; jump to escape sequence handler.
  545.  
  546. f_tloop:                                ; | If in graphics mode, jump to alternate loop
  547.                                         ; | What a massive kludge!  A better approach would have been
  548.                                         ; | to collect characters for a "write n chars" routine
  549.                                         ; | which would handle both text and graphics modes.
  550.         call    in_g_mode
  551.         jc      f_t_cloop
  552.         jmp     f_g_cloop
  553.  
  554. f_t_cloop:
  555.         LODSB                           ; get char! (al = ds:[si++])
  556.         cmp     al, 28                  ; is it a control char?
  557.         jb      f_control               ;  maybe...
  558. f_t_nctl:
  559.         seg_cs
  560.         xlat
  561.         IFDEF   snow
  562.             push    dx
  563.             mov     dx,port_6845        ; Get CRT controller port
  564.             cmp     dx,3D4H             ; Check for CGA
  565.             jnz     f_t_l3              ; ... Xfr - not CGA
  566.             add     dx,6                ; Compute adr of Status port
  567.             xchg    ax,bx               ;[PBG] quickly save ax
  568. f_t_l1:     in      al,dx               ;[PBG] Get port status
  569.             test    al,1
  570.             jnz     f_t_l1              ;[PBG] Not ready
  571. f_t_l2:     in      al,dx
  572.             test    al,1
  573.             jz      f_t_l2
  574.             xchg    ax,bx               ;[PBG] Now restore ax
  575. f_t_l3:     STOSW                       ; Put Char! (es:[di++] = ax)
  576.             pop     dx
  577.         ELSE
  578.             STOSW                       ; Put Char! (es:[di++] = ax)
  579.         ENDIF
  580.         dec     dx                      ; count down to end of line
  581.         loopnz  f_t_cloop               ; and go back for more.
  582.         jz      f_t_at_eol              ; at end of line; maybe do a crlf.
  583.         jmp     short f_loopdone
  584.  
  585. f_looploop:
  586. f_ansi_exit:                            ; in case we switched into
  587.         loopnz  f_tloop                 ; a graphics mode
  588. f_t_at_eol:
  589.         jz      f_at_eol
  590.  
  591. f_loopdone:
  592.  
  593.                                         ;--------- All done with write request -----------
  594.                                         ; DI is cursor address; cursor position in cur_y, dl.
  595.         mov     ax, cs
  596.         mov     ds, ax                  ; get our segment back
  597.         assume  ds:code
  598.  
  599.                                         ; Restore cur_x = max_x - dx + 1.
  600.         mov     al, max_x
  601.         inc     al
  602.         sub     al, dl
  603.         mov     cur_x, al
  604.                                         ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  605.         call    set_pseudocursor
  606.                                         ; Return to DOS.
  607.         xor     ax, ax                  ; No error, not busy.
  608.         ret
  609.  
  610.                                         ;---- handle control characters ----
  611.                                         ; Note: cur_x is not kept updated in memory, but can be
  612.                                         ; computed from max_x and dx.
  613.                                         ; Cur_y is kept updated in memory.
  614. f_control:
  615.         cmp     al, 27                  ; Is it an escape?
  616.         jz      f_escapex
  617.         cmp     al, 13                  ; carriage return?
  618.         jz      f_cr
  619.         cmp     al, 10                  ; line feed?
  620.         jz      f_lf
  621.         cmp     al, 9                   ; tab?
  622.         jz      f_tabx
  623.         cmp     al, 8                   ; backspace?
  624.         jz      f_bs
  625.         cmp     al, 7                   ; bell?
  626.         jz      f_bell
  627.         jmp     f_nctl                  ; then it is not a control char.
  628.  
  629. f_tabx: jmp     f_tab
  630. f_escapex:
  631.         jmp     f_escape
  632. f_in_escapex:
  633.         jmp     f_in_escape
  634.  
  635. f_bs:                                   ;----- Handle backspace -----------------
  636.                                         ; Moves cursor back one space without erasing.  No wraparound.
  637.         cmp     dl, cs:max_x            ; wrap around to previous line?
  638.         ja      fbs_wrap                ; yep; disallow it.
  639.         dec     di                      ; back up one char & attrib,
  640.         dec     di
  641.         inc     dx                      ; and note one more char left on line.
  642. fbs_wrap:
  643.         jmp     f_looploop
  644.  
  645. f_bell:                                 ;----- Handle bell ----------------------
  646.                                         ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  647.         call    beep
  648.         or      al, al                  ; clear z
  649. ; old (more portable) version:
  650. ;       mov     ax, 0e07h               ; "write bell to tty simulator"
  651. ;       mov     bx, 0                   ; "page zero, color black"
  652. ;       int     10h                     ; call BIOS
  653. ;       mov     ah, cs:cur_attrib       ; restore current attribute
  654. ;       mov     bx, cs:xlate_tab_ptr    ; restore translate table address
  655. ;       or      al, al                  ; al still 7; this clears Z.
  656.         jmp     f_looploop              ; Let main loop decrement cx.
  657.  
  658. f_cr:                                   ;----- Handle carriage return -----------
  659.                                         ; di -= cur_x<<1;               set di= address of start of line
  660.                                         ; dx=max_x+1;                   set bx= chars left in line
  661.         mov     al, cs:max_x
  662.         inc     al
  663.         sub     al, dl                  ; Get cur_x into ax.
  664.         mov     ah, 0
  665.         sub     di, ax
  666.         sub     di, ax
  667.         mov     dl, cs:max_x            ; Full line ahead of us.
  668.         inc     dx
  669.         mov     ah, cs:cur_attrib       ; restore current attribute
  670.         or      al, 1                   ; clear z
  671.         jmp     f_looploop              ; and let main loop decrement cx
  672.  
  673. f_at_eol:
  674.                                         ;----- Handle overrunning right end of screen -------
  675.                                         ; cx++;                         compensate for double loop
  676.                                         ; if (!wrap_flag) { dx++; di-=2; }
  677.                                         ; else do_crlf;
  678.         inc     cx
  679.         test    cs:wrap_flag, 1
  680.         jnz     feol_wrap
  681.         dec     di
  682.         dec     di
  683.         inc     dx
  684.         jmp     f_looploop
  685. feol_wrap:
  686.                                         ; dx=max_x+1;                   set bx= chars left in line
  687.                                         ; di -= 2*(max_x+1);
  688.                                         ; do_lf
  689.         mov     dl, cs:max_x
  690.         inc     dx
  691.         sub     di, dx
  692.         sub     di, dx
  693.                                         ; fall thru to line feed routine
  694.  
  695. f_lf:                                   ;----- Handle line feed -----------------
  696.                                         ; if (cur_y >= max_y) scroll;           scroll screen up if needed
  697.                                         ; else { cur_y++; di += max_x<<1;       else increment Y
  698.  
  699.         mov     al, cs:max_y
  700.         cmp     cs:cur_y, al
  701.         jb      flf_noscroll
  702.         call    scroll_up               ; preserves bx,cx,dx,si,di
  703.         jmp     short flf_done
  704. flf_noscroll:
  705.         inc     cs:cur_y
  706.         mov     al, cs:max_x
  707.         mov     ah, 0
  708.         inc     ax
  709.         add     ax, ax
  710.         add     di, ax
  711. flf_done:
  712.         mov     ah, cs:cur_attrib       ; restore current attribute
  713.         or      al, 1                   ; clear z
  714.         jmp     f_looploop              ; and let main loop decrement cx
  715.  
  716. f_tab:                                  ;----- Handle tab expansion -------------
  717.                                         ; Get cur_x into al.
  718.         mov     al, cs:max_x
  719.         inc     al
  720.         sub     al, dl
  721.                                         ; Calculate number of spaces to output.
  722.         push    cx                      ; save cx
  723.         mov     ch, 0
  724.         mov     cl, al                  ; get zero based x coordinate
  725.         and     cl, 7
  726.         neg     cl
  727.         add     cl, 8                   ; 0 -> 8, 1 -> 8, ... 7 -> 1
  728.         sub     dx, cx                  ; update chars-to-eol, maybe set z
  729.         pushf                           ; || save Z for main loop
  730.                                         ; ah is still current attribute.  Move CX spaces to the screen.
  731.         mov     al, ' '
  732.         call    in_g_mode               ; | graphics mode
  733.         jnc     f_tab_putc              ; |
  734.         REP     STOSW
  735.         popf                            ; || restore Z flag for main loop test
  736.         pop     cx                      ; restore cx
  737.         jmp     f_looploop              ; Let main loop decrement cx.
  738.  
  739. ;--------------- graphics mode support -----------------------
  740.  
  741. f_tab_putc:                             ; graphics mode- call putc to put the char
  742.         add     dx, cx                  ; move back to start of tab
  743. f_tp_lp:
  744.         call    putchar
  745.         dec     dx                      ; go to next cursor position
  746.         loop    f_tp_lp
  747.         popf                            ; Z set if wrapped around EOL
  748.         pop     cx
  749.         jmp     f_looploop
  750.  
  751. ;---- in_g_mode -------------
  752. ; Returns Carry set if not in a graphics mode.
  753. ; Preserves all registers.
  754.  
  755. in_g_mode proc  near
  756.         cmp     cs:video_mode, 4
  757.         jb      igm_stc
  758.         cmp     cs:video_mode, 7
  759.         jz      igm_stc
  760.         clc
  761.         ret
  762. igm_stc:
  763.         stc
  764.         ret
  765. in_g_mode endp
  766.  
  767. ;---- Where to go when a character turns out not to be special
  768. f_nctl:
  769. f_not_ansi:
  770.         call    in_g_mode
  771.         jnc     f_g_nctl                ; graphics mode
  772. f_jmptnctl:
  773.         jmp     f_t_nctl                ; text mode
  774.  
  775. ;---- Alternate main loop for graphics mode ----
  776. f_g_cloop:
  777.         LODSB                           ; get char! (al = ds:[si++])
  778.         cmp     al, 28                  ; is it a control char?
  779.         jb      f_g_control             ;  maybe...
  780. f_g_nctl:
  781.         seg_cs
  782.         xlat
  783.         call    putchar
  784.         dec     dx                      ; count down to end of line
  785.         loopnz  f_g_cloop               ; and go back for more.
  786.         jz      f_g_at_eol              ; at end of line; maybe do a crlf.
  787.         jmp     f_loopdone
  788.  
  789. f_g_control: jmp f_control
  790. f_g_at_eol: jmp f_at_eol
  791.  
  792. ;---- putchar ------------------------------------------------
  793. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  794. ; On entry, registers set up as per xy_to_regs.
  795. ; Preserves all registers.
  796. putchar proc    near
  797.         push    dx
  798.         push    cx
  799.         push    bx
  800.         push    ax
  801.                                         ; 1. Set cursor position.
  802.         mov     al, cs:max_x
  803.         inc     al
  804.         sub     al, dl
  805.         mov     cs:cur_x, al
  806.         mov     dx, cs:cur_coords       ; get X & Y into DX
  807.         xor     bx, bx                  ; choose dpy page 0
  808.         mov     ah, 2                   ; chose "Set Cursor Position"
  809.         int     10h                     ; call ROM BIOS
  810.                                         ; 2. Write char & attribute.
  811.         mov     cx, 1
  812.         pop     ax                      ; get char in AL
  813.         push    ax
  814.         mov     bl, ah                  ; attribute in BL
  815.         mov     bh, 0
  816.         mov     ah, 9
  817.         int     10h
  818.         pop     ax
  819.         pop     bx
  820.         pop     cx
  821.         pop     dx
  822.         ret
  823. putchar endp
  824.  
  825. ;---- set_pseudocursor ------------
  826. ; If in graphics mode, set pseudocursor, else set real cursor.
  827. ; Destroys DS!!!!
  828.  
  829. set_pseudocursor proc near
  830.         call    in_g_mode
  831.         jnc     pseudocursor
  832. ; old (more portable, but slower) version
  833. ;       mov     dx, cur_coords          ; get X & Y into DX
  834. ;       xor     bx, bx                  ; choose dpy page 0
  835. ;       mov     ah, 2                   ; chose "Set Cursor Position"
  836. ;       int     10h                     ; call ROM BIOS
  837.  
  838.                                         ; Write directly to 6845 cursor address register.
  839.         mov     bx, di
  840.         shr     bx, 1                   ; convert word index to byte index
  841.  
  842.         mov     dx, port_6845
  843.         mov     al, 0eh
  844.         out     dx, al
  845.  
  846.         jmp     $+2
  847.         inc     dx
  848.         mov     al, bh
  849.         out     dx, al
  850.  
  851.         jmp     $+2
  852.         dec     dx
  853.         mov     al, 0fh
  854.         out     dx, al
  855.  
  856.         jmp     $+2
  857.         inc     dx
  858.         mov     al, bl
  859.         out     dx, al
  860.  
  861.                                         ; Set cursor position in low memory.
  862.         assume  ds:abs40
  863.         mov     ax, abs40
  864.         mov     ds, ax
  865. ; Does anybody ever use anything but page zero?
  866. ;       mov     al,active_page
  867. ;       cbw
  868. ;       add     ax,ax
  869. ;       xchg    bx,ax
  870.         mov     ax, cs:cur_coords
  871.         mov     cursor_posn,ax
  872.         ret
  873.  
  874.         assume  ds:code
  875. set_pseudocursor endp
  876.  
  877.  
  878. ;---- pseudocursor --------------------------------------------------
  879. ; Writes a color 15 block in XOR at the current cursor location.
  880. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  881. ; Should be disableable- the pseudocursor slows down single-char
  882. ; writes by a factor of three.
  883. pseudocursor proc near
  884.         mov     ax, 8f16h               ; xor, color 15, ^V (small block)
  885.         call    putchar
  886.         ret
  887. pseudocursor endp
  888.  
  889. ;--------------- end of graphics mode support --------------------
  890.  
  891. dosfn8  endp
  892.  
  893. ;--- get_blank_attrib ------------------------------------------------
  894. ; Determine new attribute and character for a new blank region.
  895. ; Use current attribute, just disallow blink and underline.
  896. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  897. ; Returns result in AH, preserves all other registers.
  898. get_blank_attrib proc near
  899.         mov     ah, 0
  900.         call    in_g_mode
  901.         jnc     gb_aok                  ; if graphics mode, 0 is bkgnd
  902.  
  903.         mov     ah, cs:cur_attrib
  904.         and     ah, 7fh                 ; disallow blink
  905.         cmp     cs:video_mode, 7        ; monochrome?
  906.         jnz     gb_aok
  907.         cmp     ah, 1                   ; underline?
  908.         jnz     gb_aok
  909.         mov     ah, 7                   ; yep- set it to normal.
  910. gb_aok: ret
  911. get_blank_attrib endp
  912.  
  913.  
  914. ;---- scroll_up ---------------------------------------------------
  915. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  916. ; Moves screen up 1 line, fills the last line with blanks.
  917. ; Attribute of blanks is the current attribute sans blink and underline.
  918.  
  919. scroll_up proc  near
  920.         push    ax
  921.         push    bx
  922.         push    cx
  923.         push    dx
  924.  
  925.         call    get_blank_attrib
  926.         mov     bh, ah                  ; color to use on new blank areas
  927.         mov     al, 1                   ; AL is number of lines to scroll.
  928.         mov     ah, 6                   ; BIOS: scroll up
  929.         mov     cl, 0                   ; upper-left-x of data to scroll
  930.         mov     ch, 0                   ; upper-left-y of data to scroll
  931.         mov     dl, cs:max_x            ; lower-rite-x
  932.         mov     dh, cs:max_y            ; lower-rite-y (zero based)
  933.         int     10h                     ; call BIOS to scroll a rectangle.
  934.  
  935.         pop     dx
  936.         pop     cx
  937.         pop     bx
  938.         pop     ax
  939.         ret
  940. scroll_up endp
  941.  
  942. ;---- lookup -----------------------------------------------
  943. ; Called by getchar, peekchar, and key to see if a given key has
  944. ; been redefined.
  945. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  946. ; Returns with Z cleared if no redefinition; otherwise,
  947. ; Z is set, SI points to redefinition string, CX is its length.
  948. ; Preseves AL, all but CX and SI.
  949. ; Redefinition table organization:
  950. ;  Strings are stored in reversed order, first char last.
  951. ;  The word following the string is the character to be replaced;
  952. ;  the next word is the length of the string sans header.
  953. ; param_end points to the last byte used by the parameter buffer;
  954. ; redef_end points to the last word used by the redef table.
  955.  
  956. lookup  proc    near
  957.         mov     si, redef_end           ; Start at end of table, move down.
  958.         or      al, al
  959.         jz      lu_lp
  960.         mov     ah, 0                   ; clear extraneous scan code
  961. lu_lp:  cmp     si, param_end
  962.         jbe     lu_notfound             ; If below redef table, exit.
  963.         mov     cx, [si]
  964.         cmp     ax, [si-2]              ; are you my mommy?
  965.         jz      lu_gotit
  966.         sub     si, 4
  967.         sub     si, cx                  ; point to next header
  968.         jmp     lu_lp
  969. lu_notfound:
  970.         or      si, si                  ; clear Z
  971.         jmp     short lu_exit
  972. lu_gotit:
  973.         sub     si, 2
  974.         sub     si, cx                  ; point to lowest char in memory
  975.         cmp     al, al                  ; set Z
  976. lu_exit:
  977.         ret
  978. lookup  endp
  979.  
  980. ;---- searchbuf --------------------------------------------
  981. ; Called by getchar and peekchar to see if any characters are
  982. ; waiting to be gotten from sources other than BIOS.
  983. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  984. searchbuf proc  near
  985.                                         ; Search the stuffahead buffers.
  986.         mov     cx, 4                   ; number of buffers to check for chars
  987.         mov     bx, offset fnkey - 4
  988. sbloop: add     bx, 4                   ; point to next buffer record
  989.         mov     si, [bx].len
  990.         or      si, si                  ; empty?
  991.         loopz   sbloop                  ; if so, loop.
  992.         ret
  993. searchbuf endp
  994.  
  995. ;---- getchar -----------------------------------------------
  996. ; Returns AL = next char.
  997. ; Trashes AX, BX, CX, BP, SI.
  998. getchar proc    near
  999. gc_searchbuf:
  1000.                                         ; See if any chars are waiting in stuffahead buffers.
  1001.         call    searchbuf
  1002.         jz      gc_trykbd               ; No chars?  Try the keyboard.
  1003.                                         ; A nonempty buffer was found.
  1004.         dec     [bx].len
  1005.         dec     si
  1006.         mov     bp, [bx].adr            ; get pointer to string
  1007.         mov     al, byte ptr ds:[bp][si] ; get the char
  1008.                                         ; Recognize function key sequences, move them to highest priority
  1009.                                         ; queue.
  1010.         sub     si, 1                   ; set carry if si=0
  1011.         jc      gc_nofnkey              ; no chars left -> nothing to protect.
  1012.         cmp     bx, offset fnkey
  1013.         jz      gc_nofnkey              ; already highest priority -> done.
  1014.         or      al, al
  1015.         jnz     gc_nofnkey              ; nonzero first byte -> not fnkey.
  1016.                                         ; Found a function key; move it to highest priority queue.
  1017.         dec     [bx].len
  1018.         mov     ah, byte ptr ds:[bp][si] ; get the second byte of fn key code
  1019. gc_fnkey:
  1020.         mov     fnkey.len, 1
  1021.         mov     fnkeybuf, ah            ; save it.
  1022. gc_nofnkey:
  1023.                                         ; Valid char in AL.  Return with it.
  1024.         jmp     short gcdone
  1025.  
  1026. gc_trykbd:
  1027.                                         ; Actually get a character from the keyboard.
  1028.         mov     ah, 0
  1029.         int     16h                     ; BIOS returns with char in AX
  1030.                                         ; If it's Ctrl-break, it has already been taken care of.
  1031.         or      ax, ax
  1032.         jz      gc_trykbd
  1033.  
  1034.                                         ; Look in the reassignment table to see if it needs translation.
  1035.         call    lookup                  ; Z=found; CX=length; SI=ptr
  1036.         jnz     gc_noredef
  1037.                                         ; Okay; set up the reassignment, and run thru the translation code.
  1038.         mov     xlatseq.len, cx
  1039.         mov     xlatseq.adr, si
  1040.         jmp     gc_searchbuf
  1041. gc_noredef:
  1042.                                         ; Is it a function key?
  1043.         cmp     al, 0
  1044.         jz      gc_fnkey                ; yep- special treatment.
  1045. gcdone: ret                             ; with character in AL.
  1046.  
  1047. getchar endp
  1048.  
  1049. ;---- peekchar -----------------------------------------------
  1050. ; Returns Z if no character ready, AL=char otherwise.
  1051. ; Trashes AX, BX, CX, BP, SI.
  1052. peekchar proc   near
  1053. pc_searchbuf:
  1054.         call    searchbuf
  1055.         jz      pc_trykbd               ; No chars?  Try the keyboard.
  1056.                                         ; A nonempty buffer was found.
  1057.         dec     si
  1058.         mov     bp, [bx].adr            ; get pointer to string
  1059.         mov     al, byte ptr ds:[bp][si] ; get the char
  1060.                                         ; Valid char from buffer in AL.  Return with it.
  1061.         jmp     short pcdone
  1062. pc_trykbd:
  1063.                                         ; Actually peek at the keyboard.
  1064.         mov     ah, 1
  1065.         int     16h                     ; BIOS returns with char in AX
  1066.         jz      pcexit
  1067.                                         ; If it's control-break, it's already been taken care of.
  1068.         or      ax, ax
  1069.         jnz     pc_notbrk
  1070.         mov     ah, 0
  1071.         int     16h                     ; so get rid of it!
  1072.         jmp     short pc_trykbd
  1073. pc_notbrk:
  1074.                                         ; Look in the reassignment table to see if it needs translation.
  1075.         call    lookup                  ; Z=found; CX=length; SI=ptr
  1076.         jnz     pcdone                  ; Nope; just return the char.
  1077.                                         ; Okay; get the first code to be returned.
  1078.         add     si, cx
  1079.         mov     al, [si-1]
  1080. pcdone: or      ah, 1                   ; NZ; char ready!
  1081. pcexit: ret                             ; with character in AL, Z true if no char waiting.
  1082. peekchar endp
  1083.  
  1084. ;---- beep ------------------------------------------------------
  1085. ; Beep speaker; period given by beep_div, duration by beep_len.
  1086. ; Preserves all registers.
  1087.  
  1088. beep_div dw     1300                    ; fairly close to IBM beep
  1089. beep_len dw     3                       ; 3/18 sec- shorter than IBM
  1090.  
  1091. beep    proc    near
  1092.         push_all
  1093.  
  1094.         mov     al, 10110110b           ; select 8253
  1095.         mov     dx, 43h                 ; control port address
  1096.         out     dx, al
  1097.         dec     dx                      ; timer 2 address
  1098.         mov     ax, cs:beep_div
  1099.         jmp     $+2
  1100.         out     dx, al                  ; low byte of divisor
  1101.         xchg    ah, al
  1102.         jmp     $+2
  1103.         out     dx, al                  ; high byte of divisor
  1104.         mov     dx, 61h
  1105.         jmp     $+2
  1106.         in      al, dx                  ; get current value of control bits
  1107.         push    ax
  1108.         or      al, 3
  1109.         jmp     $+2
  1110.         out     dx, al                  ; turn speaker on
  1111.  
  1112.                                         ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1113.         push    es
  1114.         mov     ax, abs40
  1115.         mov     es, ax
  1116.         assume  es:abs40
  1117.         mov     bx, timer_low
  1118.         mov     cx, -1
  1119. beeplp: mov     ax, timer_low
  1120.         sub     ax, bx
  1121.         cmp     ax, cs:beep_len
  1122.         jg      beepover
  1123.         loop    beeplp
  1124. beepover:
  1125.         pop     es
  1126.         assume  es:code
  1127.  
  1128.                                         ; Turn off speaker
  1129.         pop     ax
  1130.         and     al, not 3               ; turn speaker off
  1131.         out     dx, al
  1132.         pop_all
  1133.         ret
  1134. beep    endp
  1135.  
  1136. CODE    ends
  1137.  
  1138.         end                             ; of nansi.asm
  1139.